#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>

//工具类
class Util{
    public:
        // 我想将换行符统一按照默认情况\n来处理，那我就要对换行符为\r和\r\n这两种情况进行特殊处理
        static int ReadLine(int sock, std::string &out)
        {
            char ch = 'X';
            while(ch != '\n'){
                // 调用recv函数，从sock接收到的信息中读取一字节到ch中
                ssize_t s = recv(sock, &ch, 1, 0);
                // s>0表示成功读到了1字节
                if(s > 0){
                    // 如果读到了\r，那么这个位置就是换行符，我们就要对这个位置进行特殊处理：
                    // 即将\r\n->\n，\r->\n
                    if(ch == '\r'){

                        // 为了判断此时的换行符究竟是\r还是\r\n，我们只能再读下一个字符
                        // 但是我们只是想看看下一个字符到底是啥，并不想直接把它读出来，怎么实现这个要求呢？
                        // 答案就是在调用recv时。将第四个参数flag设置为MSG_PEEK（窥探接收缓冲区中的第一个字符数据）
                        recv(sock, &ch, 1, MSG_PEEK);
                        // 此时我已经将窥探到的下一个字符读到了ch中，但是该字符仍然是内核接收缓冲区中的第一个字符，并没有在读取操作之后被删除
                        if(ch == '\n'){
                            // 走到这里说明换行符就是\r\n，
                            // 由于此时ch已经是\n了，我们调用recv函数再次读取缓冲区中的一个字符。第四个参数flag设置为0，表示正常读

                            recv(sock, &ch, 1, 0);
                            // 由于上次读取过后，内核接收缓冲区中的第一个字符仍然是\n，所以recv语句执行之后，ch的值仍然是\n
                            // 但是由于此时调用recv时。第四个参数flag为0，所以\n会被顺利读走，原来内核接收缓冲区中的第二个字符就变成了现在的第一个字符
                        }
                        else{
                            // 走到这里说明换行符就是\r，由于后面要push_back(ch);
                            // 我们直接把ch赋值为\n，这样就实现了\r->\n
                            ch = '\n';
                        }
                    }
                    else{
                        // 走到这里说明ch是：
                        //1. 普通字符
                        //2. \n
                        //这两种情况我们都不用特殊处理
                    }
                    // 对特殊换行符进行处理之后，直接插入即可
                    out.push_back(ch);
                    // 由于我们前面已经对\r和\r\n这两种情况进行了特殊处理，处理之后ch都是\n
                    // 如果此时ch=\n，就代表着读取过程中遇到了换行符\r、\r\n或者\n，那么再进行下一次循环的时候，while条件就不满足了，循环就退出了
                }
                else if(s == 0){
                    // s == 0说明对端关闭
                    return 0;
                }
                else{
                    // 走到这里说明读取出错了，我们返回-1
                    return -1;
                }
            }
            // std::cout<<out<<std::endl;
            // return out.size()-1; // 这里的-1是把换行符给去掉了，只计算有效数据的字节数
            return out.size();
        }
        // 这个函数用来将字符串target切分成两个子字符串sub1_out和sub2_out，切分处的位置标志就是字符串sep
        static bool CutString(const std::string &target, std::string &sub1_out, std::string &sub2_out, std::string sep)
        {
            size_t pos = target.find(sep);
            if(pos != std::string::npos){
                sub1_out = target.substr(0, pos);
                sub2_out = target.substr(pos+sep.size());
                return true;
            }
            return false;
        }
};
